﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Needle.Utils.Threading
{
    /// <summary>
    /// A simple thread bound context 
    /// </summary>
    /// <remarks>
    /// usage:
    /// <code>
    /// public class Test
    /// {
    ///     public static void Foo()
    ///     {
    ///         Action printContext = () =&gt; Console.WriteLine(Context&lt;object&gt;.Current);
    /// 
    ///         using (var context = Context&lt;object&gt;.Create("outer context"))
    ///         {
    ///             printContext();
    ///             Bar(printContext);
    ///             printContext();
    ///         }
    ///     }
    /// 
    ///     public static void Bar(Action action)
    ///     {
    ///         using (var context = Context&lt;object&gt;.Create("inner context"))
    ///         {
    ///             action();
    ///         }
    ///     }
    /// }
    /// </code>
    /// </remarks>
    public sealed class Context<T> : IDisposable
        where T: class
    {
        [ThreadStatic]
        private static T _current;

        private T _oldCurrent;
        private T _myCurrent;
        
        /// <summary>
        /// The context object.
        /// </summary>
        public static T Current
        {
            get { return _current; }
        }
        /// <summary>
        /// Creates a new context.
        /// </summary>
        public static Context<T> Create(T item)
        {
            return new Context<T>(item);
        }

        private Context(T item)
        {
            _oldCurrent = _current;
            _current = _myCurrent = item;
        }

        void IDisposable.Dispose()
        {
            if (!object.ReferenceEquals(_myCurrent, _current))
            {
                throw new ContextMismatchException("Unbalanced Create() and Dispose().");
            }
            _current = _oldCurrent;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class ContextMismatchException : Exception
    {
        public ContextMismatchException() { }
        public ContextMismatchException(string message) : base(message) { }
        public ContextMismatchException(string message, Exception innerException) : base(message, innerException) { }
        protected ContextMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }

}
